/*

  HelloWorld.ino

  Universal 8bit Graphics Library (https://github.com/olikraus/u8g2/)

  Copyright (c) 2016, olikraus@gmail.com
  All rights reserved.

  Redistribution and use in source and binary forms, with or without modification,
  are permitted provided that the following conditions are met:

  * Redistributions of source code must retain the above copyright notice, this list
	of conditions and the following disclaimer.

  * Redistributions in binary form must reproduce the above copyright notice, this
	list of conditions and the following disclaimer in the documentation and/or other
	materials provided with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/
// #define RIGHT_HAND_JOYSTICK //使用右手手柄的引脚宏定义
#define LEFT_HAND_JOYSTICK //使用左手手柄的引脚宏定义

#include <Arduino.h>
#include <U8g2lib.h>
#include "FastLED.h" // 此示例程序需要使用FastLED库
#include "OneButton.h"
//关于右手摇杆的宏定义
#ifdef RIGHT_HAND_JOYSTICK
#define BOLUN_PUSH_PIN 5	   // 拨轮按下引脚
#define UP_INPUT_PIN 12		   // UP按键的引脚编号
#define BUZZER_PIN 13		   // 蜂鸣器控制引脚编号
#define DISPLAY_EN_PIN 15	   // 用于允许电压测量，温度测量，电量显示的控制引脚，高电平使能
#define OLED_SDA_PIN 16		   // OLED屏幕的SDA引脚
#define OLED_SCL_PIN 17		   // OLED屏幕的SCL引脚
#define BOLUN_DOWN_PIN 18	   // 拨轮向下拨引脚
#define BOLUN_UP_PIN 19		   // 拨轮向上拨引脚
#define WS2812_PIN 23		   // WS2812B灯珠控制引脚编号
#define JOYSTICK_BUTTON_PIN 32 // 摇杆按下的输入引脚
#define BATTARY_MESURE_PIN 34  // 电池电压测量引脚
#define NTC_MESURE_PIN 35	   // NTC热敏电阻分压电路电压测量引脚
#define IN_Y_PIN 36			   // 可变电阻Y（垂直摇杆）的输入脚
#define IN_X_PIN 39			   // 可变电阻X（水平摇杆）的输入脚
#endif
//关于左手摇杆的宏定义
#ifdef LEFT_HAND_JOYSTICK
#define BOLUN_PUSH_PIN 27	   // 拨轮按下引脚
#define UP_INPUT_PIN 18		   // UP按键的引脚编号
#define BUZZER_PIN 12		   // 蜂鸣器控制引脚编号
#define DISPLAY_EN_PIN 15	   // 用于允许电压测量，温度测量，电量显示的控制引脚，高电平使能
#define FRONT_BUTTON_PIN 14	   // 无声按键前键
#define BEHIND_BUTTON_PIN 5   // 无声按键后键
#define LEFT_BUTTON_PIN 25	   // 无声按键左键
#define RIGHT_BUTTON_PIN 13	   // 无声按键右键
#define BOLUN_DOWN_PIN 33	   // 拨轮向下拨引脚
#define BOLUN_UP_PIN 26		   // 拨轮向上拨引脚
#define WS2812_PIN 23		   // WS2812B灯珠控制引脚编号
#define JOYSTICK_BUTTON_PIN 32 // 摇杆按下的输入引脚
#define BATTARY_MESURE_PIN 35  // 电池电压测量引脚
#define NTC_MESURE_PIN 34	   // NTC热敏电阻分压电路电压测量引脚
#define IN_Y_PIN 36			   // 可变电阻Y（垂直摇杆）的输入脚
#define IN_X_PIN 39			   // 可变电阻X（水平摇杆）的输入脚
#endif

#define Arrray_num 5		  // 资料数组元素数量
#define middle Arrray_num / 2 // 资料数组中间索引

//与U8g2库有关的宏定义
U8G2_SH1106_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, /* SCL=*/17, /* SDA=*/16, /* reset=*/U8X8_PIN_NONE);
#define MINI_LOGO
#define SUN 0
#define SUN_CLOUD 1
#define CLOUD 2
#define RAIN 3
#define THUNDER 4

int valX[Arrray_num] = {0}; // x轴资料数组
int valY[Arrray_num] = {0}; // Y轴资料数组
#define BITS 12				// PWM波解析度

#define NUM_LEDS 1		  // WS2812B灯珠数量
#define LED_TYPE WS2812B  // LED灯带型号
#define COLOR_ORDER GRB	  // RGB灯珠中红色、绿色、蓝色LED的排列顺序
uint8_t max_bright = 255; // LED亮度控制变量，可使用数值为 0 ～ 255， 数值越大则光带亮度越高
CRGB leds_D[NUM_LEDS];	  // 建立点阵leds

#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif

OneButton bolun_push_button(BOLUN_PUSH_PIN, true);		   // 实例化一个OneButton对象
OneButton bolun_up_button(BOLUN_UP_PIN, false, false);	   // 实例化一个OneButton对象
OneButton bolun_down_button(BOLUN_DOWN_PIN, false, false); // 实例化一个OneButton对象
OneButton up_input_button(UP_INPUT_PIN, true);			   // 实例化一个OneButton对象
OneButton joystick_push_button(JOYSTICK_BUTTON_PIN, true); // 实例化一个OneButton对象
#ifdef LEFT_HAND_JOYSTICK
OneButton front_button(FRONT_BUTTON_PIN, true);	  // 实例化一个OneButton对象
OneButton behind_button(BEHIND_BUTTON_PIN, true); // 实例化一个OneButton对象
OneButton left_button(LEFT_BUTTON_PIN, true);	  // 实例化一个OneButton对象
OneButton right_button(RIGHT_BUTTON_PIN, true);	  // 实例化一个OneButton对象
#endif

// 定义蜂鸣器音调结构体
typedef struct data
{
	uint16_t pitch;	   // 音高频率
	uint16_t interval; // 间隔时间
} note;				   // 自定义类型

note tones[] = { // 声明储存「音调」结构体的数组
	{650, 500},
	{400, 500}};
// 音调数组的大小
byte toneSize = sizeof(tones) / sizeof(note);

void alarmSnd()
{
	static uint8_t i = 0;
	ledcWriteTone(0, tones[i].pitch);
	delay(tones[i].interval);
	if (++i % toneSize == 0)
	{
		i = 0;
	}
}
// 蜂鸣器叫一下
void noiseSnd(uint16_t pitch)
{
	ledcWriteTone(0, pitch);
	delay(20);
}
// 蜂鸣器叫两下
void noise_doubleSnd(uint16_t pitch)
{
	ledcWriteTone(0, pitch);
	delay(20);
	ledcWriteTone(0, 0);
	delay(50);
	ledcWriteTone(0, pitch);
	delay(20);
}
void stop_alarmSnd()
{
	ledcWriteTone(0, 0);
}

int cmp(const void *a, const void *b)
{
	return (*(int *)a - *(int *)b);
}

void filter()
{
	static byte i = 0; // 陣列索引

	valX[i] = analogRead(IN_X_PIN); // 水平（X）搖桿的輸入值
	valY[i] = analogRead(IN_Y_PIN); // 垂直（Y）搖桿的輸入值

	qsort(valX, Arrray_num, sizeof(int), cmp);
	qsort(valY, Arrray_num, sizeof(int), cmp);

	if (++i % Arrray_num == 0)
		i = 0;
}

String msg_x = "";
String msg_y = "";
void control()
{
	int posX, posY; // 暫存類比輸入值的變數

	posX = map(valX[middle], 0, 4095, 0, 180); // 类似于中值滤波
	posY = map(valY[middle], 0, 4095, 0, 180); // map是重映射
	// posX = analogRead(IN_X);  // 水平（X）搖桿的輸入值
	// posY = analogRead(IN_Y);  // 垂直（Y）搖桿的輸入值
	msg_x = (String)(posX);
	msg_y = (String)(posY);

	// servoX.write(posX);    // 設定伺服馬達的旋轉角度
	// servoY.write(posY);

	//  delay(15);             // 延遲一段時間，讓伺服馬達轉到定位。
}

void drawLogo(void)
{
	u8g2.setFontMode(1); // Transparent
#ifdef MINI_LOGO

	u8g2.setFontDirection(0);
	u8g2.setFont(u8g2_font_inb16_mf);
	u8g2.drawStr(0, 22, "U");

	u8g2.setFontDirection(1);
	u8g2.setFont(u8g2_font_inb19_mn);
	u8g2.drawStr(14, 8, "8");

	u8g2.setFontDirection(0);
	u8g2.setFont(u8g2_font_inb16_mf);
	u8g2.drawStr(36, 22, "g");
	u8g2.drawStr(48, 22, "\xb2");

	u8g2.drawHLine(2, 25, 34);
	u8g2.drawHLine(3, 26, 34);
	u8g2.drawVLine(32, 22, 12);
	u8g2.drawVLine(33, 23, 12);
#else

	u8g2.setFontDirection(0);
	u8g2.setFont(u8g2_font_inb24_mf);
	u8g2.drawStr(0, 30, "U");

	u8g2.setFontDirection(1);
	u8g2.setFont(u8g2_font_inb30_mn);
	u8g2.drawStr(21, 8, "8");

	u8g2.setFontDirection(0);
	u8g2.setFont(u8g2_font_inb24_mf);
	u8g2.drawStr(51, 30, "g");
	u8g2.drawStr(67, 30, "\xb2");

	u8g2.drawHLine(2, 35, 47);
	u8g2.drawHLine(3, 36, 47);
	u8g2.drawVLine(45, 32, 12);
	u8g2.drawVLine(46, 33, 12);

#endif
}

void drawURL(void)
{
#ifndef MINI_LOGO
	u8g2.setFont(u8g2_font_4x6_tr);
	if (u8g2.getDisplayHeight() < 59)
	{
		u8g2.drawStr(89, 20, "github.com");
		u8g2.drawStr(73, 29, "/olikraus/u8g2");
	}
	else
	{
		u8g2.drawStr(1, 54, "github.com/olikraus/u8g2");
	}
#endif
}
void drawWeatherSymbol(u8g2_uint_t x, u8g2_uint_t y, uint8_t symbol)
{
	// fonts used:
	// u8g2_font_open_iconic_embedded_6x_t
	// u8g2_font_open_iconic_weather_6x_t
	// encoding values, see: https://github.com/olikraus/u8g2/wiki/fntgrpiconic

	switch (symbol)
	{
	case SUN:
		u8g2.setFont(u8g2_font_open_iconic_weather_6x_t);
		u8g2.drawGlyph(x, y, 69);
		break;
	case SUN_CLOUD:
		u8g2.setFont(u8g2_font_open_iconic_weather_6x_t);
		u8g2.drawGlyph(x, y, 65);
		break;
	case CLOUD:
		u8g2.setFont(u8g2_font_open_iconic_weather_6x_t);
		u8g2.drawGlyph(x, y, 64);
		break;
	case RAIN:
		u8g2.setFont(u8g2_font_open_iconic_weather_6x_t);
		u8g2.drawGlyph(x, y, 67);
		break;
	case THUNDER:
		u8g2.setFont(u8g2_font_open_iconic_embedded_6x_t);
		u8g2.drawGlyph(x, y, 67);
		break;
	}
}

void drawWeather(uint8_t symbol, int degree)
{
	//绘制天气符号
	drawWeatherSymbol(0, 48, symbol);
	//绘制温度
	u8g2.setFont(u8g2_font_logisoso32_tf);
	u8g2.setCursor(48 + 3, 42);
	u8g2.print(degree);
	u8g2.print("°C"); // requires enableUTF8Print()
}

/*
  Draw a string with specified pixel offset.
  The offset can be negative.
  Limitation: The monochrome font with 8 pixel per glyph
*/
void drawScrollString(int16_t offset, const char *s)
{
	static char buf[36]; // should for screen with up to 256 pixel width
	size_t len;
	size_t char_offset = 0;
	u8g2_uint_t dx = 0;
	size_t visible = 0;

	u8g2.setDrawColor(0); // clear the scrolling area
	u8g2.drawBox(0, 49, u8g2.getDisplayWidth() - 1, u8g2.getDisplayHeight() - 1);
	u8g2.setDrawColor(1); // set the color for the text

	len = strlen(s);
	if (offset < 0)
	{
		char_offset = (-offset) / 8;
		dx = offset + char_offset * 8;
		if (char_offset >= u8g2.getDisplayWidth() / 8)
			return;
		visible = u8g2.getDisplayWidth() / 8 - char_offset + 1;
		strncpy(buf, s, visible);
		buf[visible] = '\0';
		u8g2.setFont(u8g2_font_8x13_mf);
		u8g2.drawStr(char_offset * 8 - dx, 62, buf);
	}
	else
	{
		char_offset = offset / 8;
		if (char_offset >= len)
			return; // nothing visible
		dx = offset - char_offset * 8;
		visible = len - char_offset;
		if (visible > u8g2.getDisplayWidth() / 8 + 1)
			visible = u8g2.getDisplayWidth() / 8 + 1;
		strncpy(buf, s + char_offset, visible);
		buf[visible] = '\0';
		u8g2.setFont(u8g2_font_8x13_mf);
		u8g2.drawStr(-dx, 62, buf);
	}
}

void draw(const char *s, uint8_t symbol, int degree)
{
	int16_t offset = -(int16_t)u8g2.getDisplayWidth();
	int16_t len = strlen(s);

	u8g2.clearBuffer();			 // clear the internal memory
	drawWeather(symbol, degree); // draw the icon and degree only once
	for (;;)					 // then do the scrolling
	{

		drawScrollString(offset, s); // no clearBuffer required, screen will be partially cleared here
		u8g2.sendBuffer();			 // transfer internal memory to the display

		delay(20);
		offset += 2;
		if (offset > len * 8 + 1)
			break;
	}
}

String msg = "请按下一个按键吧!";
void click(void *parameter)
{
	char *str = (char *)parameter;
	msg = (String)str + "click";
	Serial.println(msg);
	noiseSnd((int)str);
}
void doubleclick(void *parameter)
{
	char *str = (char *)parameter;
	msg = (String)str + "doubleclick";
	Serial.println(msg);
	noise_doubleSnd((int)str);
}
void longPressStart(void *parameter)
{
	char *str = (char *)parameter;
	msg = (String)str + "longPressStart";
	Serial.println(msg);
	digitalWrite(DISPLAY_EN_PIN, HIGH);
	noiseSnd((int)str);
}
void duringLongPress(void *parameter)
{
	char *str = (char *)parameter;
	msg = (String)str + "duringLongPress:";
	if (bolun_push_button.isLongPressed())
	{
		Serial.print(msg);
		// Serial.println(bolun_push_button.getNumberClicks());
	}
}
void longPressStop(void *parameter)
{
	char *str = (char *)parameter;
	msg = (String)str + "longPressStop";
	Serial.println(msg);
	digitalWrite(DISPLAY_EN_PIN, LOW);
	noiseSnd((int)str);
}

void taskOLED(void *pvParam)
{ // 显示器任务
	u8g2.begin();
	u8g2.enableUTF8Print();

	while (1)
	{
		u8g2.setFont(u8g2_font_wqy14_t_chinese3);
		u8g2.setFontDirection(0);
		u8g2.clearBuffer(); // clear the internal memory

		u8g2.setCursor(0, 60);
		u8g2.print("X是:");
		u8g2.print(msg_x.c_str());

		u8g2.setCursor(50, 60);
		u8g2.print("Y是:");
		u8g2.print(msg_y.c_str());

		u8g2.setCursor(0, 45);
		u8g2.print(msg);

		drawLogo();
		drawURL();
		// draw("What a beautiful day!", SUN, 27);
		// draw("The sun's come out!", SUN_CLOUD, 19);
		// draw("It's raining cats and dogs.", RAIN, 8);
		// draw("That sounds like thunder.", THUNDER, 12);
		// draw("It's stopped raining", CLOUD, 15);
		u8g2.sendBuffer(); // transfer internal memory to the display
		vTaskDelay(pdMS_TO_TICKS(1));
	}
}

void setup(void)
{
	// initialize digital pin LED_BUILTIN as an output.
	LEDS.addLeds<LED_TYPE, WS2812_PIN, COLOR_ORDER>(leds_D, NUM_LEDS); // 初始化点阵
	FastLED.setBrightness(max_bright);
	pinMode(LED_BUILTIN, OUTPUT);
	pinMode(BUZZER_PIN, OUTPUT);
	pinMode(UP_INPUT_PIN, INPUT_PULLUP);
	pinMode(DISPLAY_EN_PIN, OUTPUT);
	ledcSetup(0, 2000, BITS);	  // PWM设置为2KHz,10bit解析度
	ledcAttachPin(BUZZER_PIN, 0); // 将0号PWM波通道分配BUZZER_PIN引脚
	Serial.begin(115200);

	analogSetAttenuation(ADC_11db); // 設定類比輸入電壓上限3.6V
	analogSetWidth(BITS);

	bolun_push_button.reset(); //清除一下按钮状态机的状态
	bolun_push_button.attachClick(click, (void *)"按下");
	bolun_push_button.attachDoubleClick(doubleclick, (void *)"按下");
	bolun_push_button.attachLongPressStart(longPressStart, (void *)"按下");
	bolun_push_button.attachDuringLongPress(duringLongPress, (void *)"按下");
	bolun_push_button.attachLongPressStop(longPressStop, (void *)"按下");

	bolun_up_button.reset(); //清除一下按钮状态机的状态
	bolun_up_button.attachClick(click, (void *)"向上");
	bolun_up_button.attachDoubleClick(doubleclick, (void *)"向上");
	bolun_up_button.attachLongPressStart(longPressStart, (void *)"向上");
	bolun_up_button.attachDuringLongPress(duringLongPress, (void *)"向上");
	bolun_up_button.attachLongPressStop(longPressStop, (void *)"向上");

	bolun_down_button.reset(); //清除一下按钮状态机的状态
	bolun_down_button.attachClick(click, (void *)"向下");
	bolun_down_button.attachDoubleClick(doubleclick, (void *)"向下");
	bolun_down_button.attachLongPressStart(longPressStart, (void *)"向下");
	bolun_down_button.attachDuringLongPress(duringLongPress, (void *)"向下");
	bolun_down_button.attachLongPressStop(longPressStop, (void *)"向下");

	up_input_button.reset(); //清除一下按钮状态机的状态
	up_input_button.attachClick(click, (void *)"上侧键");
	up_input_button.attachDoubleClick(doubleclick, (void *)"上侧键");
	up_input_button.attachLongPressStart(longPressStart, (void *)"上侧键");
	up_input_button.attachDuringLongPress(duringLongPress, (void *)"上侧键");
	up_input_button.attachLongPressStop(longPressStop, (void *)"上侧键");

	joystick_push_button.reset(); //清除一下按钮状态机的状态
	joystick_push_button.attachClick(click, (void *)"JOY");
	joystick_push_button.attachDoubleClick(doubleclick, (void *)"JOY");
	joystick_push_button.attachLongPressStart(longPressStart, (void *)"JOY");
	joystick_push_button.attachDuringLongPress(duringLongPress, (void *)"JOY");
	joystick_push_button.attachLongPressStop(longPressStop, (void *)"JOY");

#ifdef LEFT_HAND_JOYSTICK
	front_button.reset(); //清除一下按钮状态机的状态
	front_button.attachClick(click, (void *)"前进");
	front_button.attachDoubleClick(doubleclick, (void *)"前进");
	front_button.attachLongPressStart(longPressStart, (void *)"前进");
	front_button.attachDuringLongPress(duringLongPress, (void *)"前进");
	front_button.attachLongPressStop(longPressStop, (void *)"前进");

	behind_button.reset(); //清除一下按钮状态机的状态
	behind_button.attachClick(click, (void *)"后退");
	behind_button.attachDoubleClick(doubleclick, (void *)"后退");
	behind_button.attachLongPressStart(longPressStart, (void *)"后退");
	behind_button.attachDuringLongPress(duringLongPress, (void *)"后退");
	behind_button.attachLongPressStop(longPressStop, (void *)"后退");

	left_button.reset(); //清除一下按钮状态机的状态
	left_button.attachClick(click, (void *)"向左");
	left_button.attachDoubleClick(doubleclick, (void *)"向左");
	left_button.attachLongPressStart(longPressStart, (void *)"向左");
	left_button.attachDuringLongPress(duringLongPress, (void *)"向左");
	left_button.attachLongPressStop(longPressStop, (void *)"向左");

	right_button.reset(); //清除一下按钮状态机的状态
	right_button.attachClick(click, (void *)"向右");
	right_button.attachDoubleClick(doubleclick, (void *)"向右");
	right_button.attachLongPressStart(longPressStart, (void *)"向右");
	right_button.attachDuringLongPress(duringLongPress, (void *)"向右");
	right_button.attachLongPressStop(longPressStop, (void *)"向右");
#endif

	xTaskCreate(taskOLED, "display task", 1500, NULL, 1, NULL);
}

CHSV my_hsv_color(0, 255, 255);
void loop(void)
{
	bolun_push_button.tick();
	bolun_up_button.tick();
	bolun_down_button.tick();
	up_input_button.tick();
	joystick_push_button.tick();
#ifdef LEFT_HAND_JOYSTICK
	front_button.tick();
	behind_button.tick();
	left_button.tick();
	right_button.tick();
#endif
	filter();
	control();
	fill_solid(leds_D, 1, my_hsv_color);
	my_hsv_color.h++;
	FastLED.show();
	stop_alarmSnd();
	// if (digitalRead(UP_INPUT_PIN) == HIGH)
	// {
	//   alarmSnd();
	// }
	// if (digitalRead(BOLUN_UP_PIN) == LOW)
	// {
	//   alarmSnd();
	// }
	// else
	// {
	//   stop_alarmSnd();
	// }
	digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
	digitalWrite(LED_BUILTIN, LOW);	 // turn the LED off by making the voltage LOW
}
